﻿using System;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Xml.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using System.ComponentModel;
using CMS.Web.Data;

namespace CMS.Web.Engine
{
    public enum ZoneTag { Td, Div }
    public static class HtmlHelperExtensions
    {
        public static MvcHtmlString Zone(this HtmlHelper helper, IPageModel model, string zone)
        {
            return helper.Zone(model, zone, ZoneTag.Div, null);
        }
        public static MvcHtmlString Zone(this HtmlHelper helper, IPageModel model, string zone, ZoneTag tag)
        {
            return helper.Zone(model, zone, tag, null);
        }
        public static MvcHtmlString Zone(this HtmlHelper helper, IPageModel model, string zone, ZoneTag tag, object htmlAttributes)
        {
            //TODO: Надо подумать, где показать виджеты, которые лежат на переименованных панелях либо как уведомить пользователя
            StringBuilder html = new StringBuilder();           
            
            html.AppendFormat("<{0} class=\"osnovacms-zone\"", tag.ToString().ToLower());
            if (htmlAttributes != null)
            {
                foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(htmlAttributes))
                    html.AppendFormat(" {0}=\"{1}\"", property.Name, property.GetValue(htmlAttributes));                    
            }
            html.Append(">");
            html.AppendFormat("<div class=\"osnovacms-zone-header\" title=\"Добавить виджет\">{0}</div>", zone);
           
            foreach (IWidget widget in model.Widgets.OnZone(zone))
            {
                WidgetAttribute widgetAnnotation = (WidgetAttribute)widget.GetType().GetCustomAttributes(typeof(WidgetAttribute), false).SingleOrDefault();
                html.AppendFormat("<div widget=\"{0}\" class=\"osnovacms-widget\">", widget.ID);
                html.AppendFormat("<div class=\"osnovacms-widget-header\">{0}</div>", widgetAnnotation.Description);               
                html.AppendFormat(helper.Widget(widget).ToHtmlString());
                html.AppendFormat("<div class=\"osnovacms-widget-edit\">Настройка</div>");
                html.AppendFormat("<div class=\"osnovacms-widget-delete\">Удалить</div>");
                html.AppendFormat("</div>");
            }

            html.AppendFormat("</{0}>", tag.ToString().ToLower());
            return new MvcHtmlString(html.ToString());
        }
        public static MvcHtmlString Widget(this HtmlHelper helper, IWidget widget)
        {           
            ViewDataDictionary viewData = new ViewDataDictionary { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = widget.Model.Prefix } };
            return helper.Partial(widget.PartialViewName, widget.Model, viewData);           
        }

        public static MvcHtmlString WidgetSetting(this HtmlHelper helper, WidgetSettingsEditorModel setting, string prefix)
        {
            StringBuilder html = new StringBuilder();
            html.AppendFormat("<td>{0}:</td>", setting.Annotation.Title);
            html.Append("<td>");
            if (setting.Annotation.ControlType == WidgetSettingControlType.CheckBox)                       
                html.Append(helper.CheckBox(string.Format("{0}.{1}", prefix, setting.Name), setting.Value.Convert<bool>(false), new { title = setting.Annotation.Description }).ToHtmlString());
            else
                html.Append(helper.TextBox(string.Format("{0}.{1}", prefix, setting.Name), setting.Value, new { title = setting.Annotation.Description }).ToHtmlString());

            html.Append("</td>");
            return new MvcHtmlString(html.ToString());
        }

        public static MvcHtmlString ValidationMessageFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, string prefix, Expression<Func<TModel, TProperty>> expression)
        {
            return ValidationMessageFor(htmlHelper, prefix, expression, null);
        }
        public static MvcHtmlString ValidationMessageFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, string prefix, Expression<Func<TModel, TProperty>> expression, string validationMessage)
        {
            return ValidationMessageFor(htmlHelper, prefix, expression, validationMessage);
        }
        public static MvcHtmlString ValidationMessageFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, string prefix, Expression<Func<TModel, TProperty>> expression, string validationMessage, object htmlAttributes)
        {
            return ValidationMessageFor(htmlHelper, prefix, expression, validationMessage, htmlAttributes);
        }
        public static MvcHtmlString ValidationMessageFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, string prefix, Expression<Func<TModel, TProperty>> expression, string validationMessage, IDictionary<string, object> htmlAttributes)
        {
            return htmlHelper.ValidationMessage(string.Format("{0}.{1}", prefix, ExpressionHelper.GetExpressionText(expression)), validationMessage, htmlAttributes);
        }
        public static MvcHtmlString HiddenFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, string prefix, Expression<Func<TModel, TProperty>> expression)
        {
            return HiddenFor(htmlHelper, prefix, expression, (IDictionary<string, object>)null);
        }
        public static MvcHtmlString HiddenFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, string prefix, Expression<Func<TModel, TProperty>> expression, object htmlAttributes)
        {
            return HiddenFor(htmlHelper, prefix, expression, htmlAttributes);
        }
        public static MvcHtmlString HiddenFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, string prefix, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes)
        {
            return htmlHelper.Hidden(string.Format("{0}.{1}", prefix, ExpressionHelper.GetExpressionText(expression)),
                ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData).Model, htmlAttributes);           
        }
        public static MvcHtmlString TextAreaFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, string prefix, Expression<Func<TModel, TProperty>> expression)
        {
            return TextAreaFor(htmlHelper, prefix, expression, (IDictionary<string, object>)null);
        }
        public static MvcHtmlString TextAreaFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, string prefix, Expression<Func<TModel, TProperty>> expression, object htmlAttributes)
        {
            return TextAreaFor(htmlHelper, prefix, expression, htmlAttributes);
        }
        public static MvcHtmlString TextAreaFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, string prefix, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes)
        {
            if (expression.IsNull())           
                throw new ArgumentNullException("expression");         
            
            var modelMetadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
            string value = !modelMetadata.IsNull() ? modelMetadata.Model.ToString() : string.Empty;

            return htmlHelper.TextArea(string.Format("{0}.{1}", prefix, ExpressionHelper.GetExpressionText(expression)), value, htmlAttributes);
        }
        public static MvcHtmlString TextBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, string prefix, Expression<Func<TModel, TProperty>> expression)
        {
            return TextBoxFor(htmlHelper, prefix, expression, (IDictionary<string, object>)null);
        }
        public static MvcHtmlString TextBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, string prefix, Expression<Func<TModel, TProperty>> expression, object htmlAttributes)
        {
            return TextBoxFor(htmlHelper, prefix, expression, htmlAttributes);
        }
        public static MvcHtmlString TextBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, string prefix, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes)
        {
            return htmlHelper.TextBox(string.Format("{0}.{1}", prefix, ExpressionHelper.GetExpressionText(expression)),
                ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData).Model, htmlAttributes);
        }
    }

    public static class ObjectExtensions
    {
        public static bool IsNull(this object value)
        {
            return (value == null);
        }
        public static T GetValueOrDefault<T>(this object value)
        {
            return value.GetValueOrDefault<T>(default(T));
        }
        public static T GetValueOrDefault<T>(this object value, T defaultValue)
        {
            try
            {
                if (!value.IsNull())
                    return (T)value;
                return defaultValue;
            }
            catch
            {
                return defaultValue; 
            }
        }
        public static T Convert<T>(this object value)
        {
            return value.Convert<T>(default(T));
        }
        public static T Convert<T>(this object value, T defaultValue)
        {
            try
            {
                return System.Convert.ChangeType(value, typeof(T)).GetValueOrDefault(defaultValue);
            }
            catch
            {
                return defaultValue; 
            }
        }
    }
    public static class StringExtensions
    {
        public static bool IsDigit(this string value)
        {
            return Regex.IsMatch(value, "^[0-9]+$");
        }
        public static bool IsEmpty(this string value)
        {
            return string.IsNullOrEmpty(value);
        }
        public static bool IsDigitOrEmpty(this string value)
        {
            return value.IsDigit() || value.IsEmpty();
        }
        public static string[] Split(this string value, string separator)
        {
            return Regex.Split(value, separator);
        }
    }
    public static class IEnumerableIWidgetExtensions
    {
        public static IWidget Find(this IEnumerable<IWidget> value, int? ID)
        {
            if (!ID.HasValue)
                throw new Exception("Не передан идентификатор виджета.");

            IWidget widget = value.SingleOrDefault(w => w.ID.Equals(ID.Value));

            if (widget.IsNull())
                throw new Exception("Виджет не найден: {0}.".Args(ID.Value));

            return widget;
        }

        public static IEnumerable<IWidget> OnZone(this IEnumerable<IWidget> value, string zone)
        {
            return value
                .Where(x => x.Zone.Equals(zone, StringComparison.InvariantCultureIgnoreCase))
                .OrderBy(x => x.Order);            
        }
    }
    public static class IEnumerableStringExtensions
    {
        public static string Join(this IEnumerable<string> value, string separator)
        {
            return string.Join(separator, value);
        }
        public static string Args(this string value, params object[] args)
        {
            return string.Format(value, args);
        }
    }
    public static class XElementExtensions
    {
        public static T AttributeValue<T>(this XElement xElement, XName name)
        {
            if (!xElement.Attribute(name).IsNull())
                return (T)Convert.ChangeType(xElement.Attribute(name).Value, typeof(T));
            return default(T);
        }
        public static void AttributeValue(this XElement xElement, XName name, object value)
        {
            if (!xElement.Attribute(name).IsNull())
                xElement.Attribute(name).Value = value.ToString();            
        } 
    }
}